import math
import pprint
import time
import sys
import random

sys.path.append("../")  # noqa
import elasticsearch.exceptions

from task_utils.task_alumni_rank_score import job_person_score
from task_utils.task_jobs import job_alumni_belong_dept, job_join_act_contacts, job_institution_to_act

from datetime import datetime, timedelta
from typing import List, Optional, Union
from elasticsearch_dsl import Q
from bson import ObjectId

from api.search.import_activity import clean_media, clean_region, clean_dept, region_map as region_map2

from api.search.schema_act_member import Activity, Register
from api.search.schema_alumni import Member
from api.search.schema_institution import Department
from api.search.utils import activity_import, role_import
from config import col_alumni, col_register, col_activity, col_institution, settings, aredis
from core.es import es_client, Index_KJJ_ALUMNI, Index_KJJ_DEPARTMENT, Index_KJJ_ACTIVITY_REGISTER
from task_utils.task_activity_span_year import task_update_activity_span
from task_utils.task_institution import task_accumulate_metric
from task_utils.task_institution_overview import task_person_reputation, task_update_inst_alumni_agg, \
    task_update_inst_act_agg
from task_utils.task_radar import task_radar, update_alumni_score
from task_utils.util_mapping import ORG_LIST, mapping_region
from core import strutil
from worker_celery import celery_app
from elasticsearch import Elasticsearch

es_client = Elasticsearch(settings.dsl, timeout=120)

_KEY = f"ranker"


def region_map(region):
    if not region:
        return ""
    province = region
    if not province:
        return ""
    for k, v in mapping_region.items():
        checker = [item for item in v if province.strip() in item]
        if checker:
            return k


@celery_app.task(default_retry_delay=10, max_retry=3, soft_time_limit=30)
def workflow_creat_edit_alumni(person_ids: List[str]):
    """创建/编辑人才"""''
    for person_id in person_ids:
        job_person_score(person_id)
        job_update_alumni_to_es(person_id)


@celery_app.task(default_retry_delay=2, max_retry=2, soft_time_limit=30)
def workflow_delete_alumni(person_ids: List[str]):
    """删除人才, 更新活动关联的人次的状态
    """
    job_delete_alumni(person_ids)
    for person_id in person_ids:
        job_person_score(person_id)
    act_ids = col_register.find({"person_id": {"$in": [ObjectId(p) for p in person_ids]}}).distinct("activity_id")

    for act_id in act_ids:
        partial_update_activity_metric(str(act_id))


@celery_app.task(default_retry_delay=10, max_retry=3, soft_time_limit=30)
def workflow_cancel_contacts(act_id: str, person_id: str):
    """解除关联, 延迟高点"""
    # chain
    job_delete_contact_by_act(act_id, person_id)
    partial_update_alumni_metric(person_id)
    partial_update_activity_metric(act_id)


@celery_app.task(default_retry_delay=10, max_retry=3, soft_time_limit=30)
def workflow_creat_edit_activity(act_ids: List[str]):
    """
    编辑/创建活动
    """
    # 考虑权限是否变化的问题
    # 权限更新

    for act in act_ids:
        job_update_activity(act)
    # todo 权限也不要变更
    person_ids = col_register.find({"activity_id": {"$in": [ObjectId(act) for act in act_ids]}}).distinct("person_id")
    for person_id in person_ids:
        job_person_score(person_id)
        time.sleep(.1)
        partial_update_alumni_metric(str(person_id))


@celery_app.task(default_retry_delay=10, max_retry=5, soft_time_limit=30)
def workflow_delete_act(act_ids: List[str], person_ids: List[str] = None):
    """删除活动"""''
    for act in act_ids:
        job_delete_contact_by_act(act=act)
        partial_update_activity_metric(act)

    for person_id in person_ids:
        job_person_score(person_id)  # 更新得分+redis
        partial_update_alumni_metric(person_id)


@celery_app.task(default_retry_delay=10, max_retry=5, soft_time_limit=30)
def workflow_alumni_join_activity(act_id: str, person_ids: List[str]):
    """todo 活动> 关联人才, 如果活动没有在es"""

    job_join_act_contacts([act_id])  # write mongo
    partial_update_activity_metric(act_id)
    # 人可读权限
    job_institution_to_act([act_id])

    for person_id in person_ids:
        # 插入报名信息

        time.sleep(.06)
        job_update_activity_contacts(act_id, person_id)
        partial_update_alumni_metric(person_id)


@celery_app.task()
def job_delete_alumni(person_ids: List[str]):
    """删除人才索引数据"""
    query = {"query": {"ids": {"values": person_ids}}}
    aredis.zrem(_KEY, *person_ids)
    es_client.delete_by_query(index=Index_KJJ_ALUMNI, body=query, conflicts='proceed')
    # 全局更
    score_alumni_by_redis()
    return True


@celery_app.task()
def job_delete_institutions(inst_ids: List[str]):
    """删除机构"""
    query = {"query": {"ids": {"values": inst_ids}}}
    es_client.delete_by_query(index=Index_KJJ_DEPARTMENT, body=query)
    return True


@celery_app.task(name="es-delete-activities")
def job_delete_activity(act_ids: List[str]):
    """包括删除活动或者联系报名人"""
    if act_ids:
        delete_contact_query = {
            "query": {
                "bool": {
                    "should": [
                        {

                            "terms": {
                                "activity_id": act_ids
                            }
                        },
                        {"ids": {"values": act_ids}}
                    ]
                }
            }
        }
        res = es_client.delete_by_query(index=Index_KJJ_ACTIVITY_REGISTER, body=delete_contact_query,
                                        wait_for_completion=False)
    return res


@celery_app.task(name="es-delete-contact")
def job_delete_contact_by_act(act: str, person=None):
    """根据活动 删除解除关联"""
    query = {
        "query": {
            "bool": {
                "must": [
                    {
                        "has_parent": {
                            "parent_type": "activity",
                            "query": {
                                "match": {
                                    "id": act
                                }
                            }
                        }
                    }

                ],
                "filter": []
            }
        }
    }
    if person:
        query['query']['bool']['filter'].append(
            {
                "term": {
                    "person_id": person
                }
            }
        )
    res = es_client.delete_by_query(index=Index_KJJ_ACTIVITY_REGISTER, body=query, conflicts="proceed")

    return res


@celery_app.task(name="es-delete-ref-alumni")
def job_check_orphan_contact_and_del_from_es(person_id: str):
    has_activities = col_register.count_documents({"person_id": ObjectId(person_id), "is_attended": True})

    if not has_activities or person_id:
        query = {"query": {"ids": {"values": [person_id]}}}
        aredis.zrem(_KEY, person_id)

        es_client.delete_by_query(index=Index_KJJ_ALUMNI, body=query)


@celery_app.task(name="es-update-alumni")
def job_update_alumni_to_es(person_id: str):
    """更新或者创建人才索引数据"""
    alumni = Member(_id=person_id, id=person_id)
    alumni_doc = col_alumni.find_one({"_id": ObjectId(person_id), "is_deleted": {"$ne": True}})
    if alumni_doc:
        register_docs = col_register.find({"person_id": ObjectId(person_id), "is_attended": True},
                                          {"activity_id": 1, "level_of_role": 1})
        # 计算总分
        register = list(register_docs)
        _score = []
        if register:
            for reg in register:
                act = col_activity.find_one({"_id": reg["activity_id"]})
                if act:
                    score_act = act.get("importance_of_activities", "常规")
                    score_role = reg.get("level_of_role", "一般参与")
                    active_score = activity_import.get(score_act, 1) * role_import.get(score_role, 1)
                    _score.append(active_score)

        _register = {act.get("activity_id") for act in register}

        number_of_applicants = len(_register) if _register else 0
        alumni.number_of_applicants = number_of_applicants
        _sum_score = sum(_score)
        alumni.active = _sum_score
        alumni.activation = _sum_score

        alumni_rate, alumni_ranking = score_alumni_score(person_id)
        alumni.rank_alumni_score = alumni_ranking  # 名次
        alumni.prank_alumni_score = alumni_rate  # 名次排率
        alumni.person_id = person_id
        alumni.name_zh = alumni_doc.get("name_zh") or alumni_doc.get("name")
        alumni.name = alumni_doc.get("name") or alumni_doc.get("name_zh")
        alumni.identify_type = alumni_doc.get("identify_type")
        alumni.gender = alumni_doc.get("gender")
        alumni.age = alumni_doc.get("age")
        alumni.nation = alumni_doc.get("nation")
        alumni.professional_titles = alumni_doc.get("professional_titles")
        alumni.job_title = alumni_doc.get("job_title")
        alumni.research_interests = alumni_doc.get("academic_area")
        alumni.job_province = alumni_doc.get("job_province")
        alumni.job_city = alumni_doc.get("job_city")
        alumni.subject = alumni_doc.get("subject", [])
        alumni.email = alumni_doc.get("email", [])
        alumni.cur_institution_name = alumni_doc.get("cur_institution_name")
        alumni.cur_institution_department = alumni_doc.get("cur_institution_department")
        alumni.identify_photo = alumni_doc.get("identify_photo")
        alumni.degree = alumni_doc.get("academic_qualification")  # 最高学历
        alumni.graduate_school = alumni_doc.get("graduate_school")
        alumni.academic_qualification = alumni_doc.get("academic_qualification")
        alumni.political_status = alumni_doc.get("political_status")
        alumni.region = region_map(alumni_doc.get("job_province"))
        alumni.alumni_score = task_person_reputation(alumni_doc.get("_id"))  # fixme 计算身份价值 alumni_score
        alumni.update_at = datetime.utcnow()

        alumni.belongs_inst = alumni_doc.get("belongs_inst", [-1])
        name = alumni_doc.get('name', alumni_doc.get('name_zh'))
        if strutil.contain_zh(name):
            alumni.name_pinyin = strutil.cn_name2pinyin(name)

        source = alumni.to_dict(True)
        body = {"doc": source['_source'], "doc_as_upsert": True}

        alumni._get_connection().update(
            index=Member.Index().name, id=person_id, body=body,
            refresh=True,
            retry_on_conflict=5
        )


@celery_app.task(name="es-update-activity")
def job_update_activity(act_id: str):
    """创建更新活动数据"""
    act_doc = col_activity.find_one({"_id": ObjectId(act_id)})

    _year = act_doc['end_time'].year if isinstance(act_doc['end_time'], datetime) else None

    activity = Activity(
        _id=str(act_doc.get("_id")),
        id=str(act_doc.get("_id")),
        name=act_doc.get('name'),
        start_time=act_doc.get('start_time'),
        end_time=act_doc.get('end_time'),
        range_year=_year,
        abstract=act_doc.get('abstract'),
        activity_type=act_doc.get('activity_type'),
        activity_type_two=act_doc.get('activity_type_two'),
        images=clean_media(act_doc.get('images', [])),
        videos=clean_media(act_doc.get('video', [])),
        province=clean_region(act_doc.get("region"))[0],
        city=clean_region(act_doc.get("region"))[1],
        district=clean_region(act_doc.get("region"))[2],
        update_time=act_doc.get("update_time"),
        update_at=datetime.utcnow(),
        host_label=clean_dept(act_doc['institution_id'])[0],
        coop_labels=clean_dept(act_doc['co_inst_ids']),
        region=region_map2(act_doc.get("region", None)),
        importance_of_activities=act_doc.get("importance_of_activities", "常规"),
        activity_tags=act_doc.get("tags", []),
        score_importance_of_activities=activity_import.get(act_doc.get("importance_of_activities", "常规")) or 1,
        meta={"routing": 1}
    )

    activity.save()

    members = col_register.count_documents({"activity_id": act_doc['_id'], "is_attended": True})

    _number_of_applicants = members

    activity.number_of_applicants = _number_of_applicants
    source = activity.to_dict(True)
    #  "activity_member" : "activity",
    source['activity_member'] = 'activity'
    body = {"doc": source['_source'], "doc_as_upsert": True}
    es_client.update(index=Index_KJJ_ACTIVITY_REGISTER, id=act_id, routing=1,
                     body=body, refresh=True, retry_on_conflict=10)


@celery_app.task(name="es-update-contacts")
def job_update_activity_contacts(act_id: str, person_id: str = None):
    """ 更新报名信息"""
    activity = Activity.get(id=act_id, ignore=404)
    act_doc = col_activity.find_one({"_id": ObjectId(act_id), "is_deleted": {"$ne": True}})
    query = {"activity_id": ObjectId(act_id), "is_attended": True}

    if person_id:
        query['person_id'] = ObjectId(person_id)
    if activity and act_doc:
        members = col_register.find(query)
        for member in members:
            update_data = {
                "contribution": member.get("contribution"),
                "is_attended": member.get("is_attended"),
                "level_of_role": member.get("level_of_role"),
                "per_act_time": member.get("per_act_time", 0),
                "update_time": member.get("update_time"),
                "person_id": str(member.get("person_id")),
                "activity_id": str(member.get("activity_id")),
                "act_type": str(act_doc.get('activity_type')),
                "score_importance_of_activities": activity_import.get(
                    act_doc.get("importance_of_activities", "常规")) or 1,
                "score_importance_of_role": role_import.get(member.get("level_of_role", "一般参与")),
                "update_at": datetime.utcnow(),
                "activity_member": {"name": "member", "parent": act_id},
            }
            body = {"doc": update_data, "doc_as_upsert": True}
            es_client.update(Index_KJJ_ACTIVITY_REGISTER, id=person_id, routing=1, body=body,
                             retry_on_conflict=3)

    score_alumni_by_redis()


@celery_app.task(name="es-update-institution")
def job_update_institution(inst_id: str):
    inst_id = ObjectId(inst_id)
    dept = col_institution.find_one({"_id": inst_id})

    act_ids = col_activity.find({
        "$or": [{"institution_id": inst_id}, {"co_inst_ids": inst_id}],
        "is_deleted": {"$ne": True}}
    ).distinct('_id')

    person_counts = 0
    if act_ids:
        act_ids = act_ids
        person_counts = col_register.count_documents({"activity_id": {"$in": act_ids}, "is_attended": True})
    acc_activity, acc_alumni, acc_join, acc_institution = task_accumulate_metric(inst_id, True)

    department = Department(
        _id=str(inst_id),
        id=str(inst_id),
        institution_name=dept.get("institution_name"),
        in_kexie=dept.get("in_kexie", False),
        level=dept.get("level", -1),
        self_kexie=True if dept.get("level", -1) >= 0 else False,
        parent_institution_name=dept.get("parent_institution_name"),
        establishment_time=dept.get("establishment_time"),
        institution_type=dept.get("institution_type"),
        institution_location=dept.get("institution_location", ""),
        address=dept.get("region"),
        participants=person_counts,
        activities_of_participants=len(act_ids) if act_ids else 0,
        oindex=dept.get("oindex"),
        m_activity_overview=dept.get("m_activity_overview"),  # 组织活动模块
        m_contact_overview=dept.get("m_contact_overview"),  # 活动联系人才模块
        m_activity_span=dept.get("m_activity_span"),  # 活动1-5年持续统计
        create_at=datetime.utcnow(),
        update_at=datetime.utcnow(),
        acc_join=acc_join,
        acc_alumni=acc_alumni,
        acc_institution=acc_institution,
        acc_activity=acc_activity,
    )

    department.save()


# 下面是定时周期任务
@celery_app.task()
def job_task_update_activity_span():
    """每周一次"""
    start_year = datetime.utcnow().year
    task_update_activity_span(start_year=start_year)


@celery_app.task()
def job_task_update_dept_to_es():
    """todo 更新部门的统计指标 duration: 1s"""
    for dept in ORG_LIST:
        task_accumulate_metric(dept, sync=True)
        doc = col_institution.find_one({"_id": ObjectId(dept)})
        dept = Department.get(id=dept, ignore=404)
        if dept:
            dept.update(m_activity_span=doc.get("m_activity_span"),
                        m_activity_overview=doc.get("m_activity_overview"),
                        m_contact_overview=doc.get("m_contact_overview"),
                        acc_alumni=doc.get("acc_alumni"),
                        acc_join=doc.get("acc_join"),
                        oindex=doc.get("oindex"),
                        acc_activity=doc.get("acc_activity"),
                        served_people_num=doc.get("served_people_num"),
                        update_at=datetime.utcnow(),
                        retry_on_conflict=5,
                        )


@celery_app.task()
def job_alumni_rank_score_to_es():
    """计算身份价值 alumni_score"""


@celery_app.task()
def job_task_radar():
    """每周一次"""
    start_year = datetime.utcnow().year
    task_radar(start_year)


@celery_app.task()
def job_task_alumni_rank_score(person_ids: List[str] = None):
    """批量更新 人才的指标统计: 身份 排名 （埋点：活动管理的人接口，人列表）"""
    for person_id in person_ids:

        alumni = Member.get(id=person_id, ignore=404)
        if alumni:
            register_docs = col_register.find({"person_id": ObjectId(person_id), "is_attended": True})
            register = list(register_docs)
            alumni_doc = col_alumni.find_one({'_id': ObjectId(person_id)},
                                             {"rank_alumni_score": 1, "rank_alumni_score": 1})

            _register = {act.get("activity_id") for act in register}
            alumni.number_of_applicants = len(_register) if _register else 0  # 累计参与活动
            _score = []
            for reg in register:
                act = col_activity.find_one({"_id": reg["activity_id"], "is_attended": True})
                if act:
                    score_act = act.get("importance_of_activities", "常规")
                    score_role = reg.get("level_of_role", "一般参与")
                    active_score = activity_import.get(score_act, 1) * role_import.get(score_role, 1)
                    _score.append(active_score)

            _sum_score = sum(_score)
            # 写入es
            _key = f"ranker"
            offset = random.random()
            t2 = int((time.time() + offset) * 1000_1000)
            score = f"{_sum_score}.{t2}"

            _mapping = {person_id: float(score)}

            aredis.zadd(_key, _mapping)

            alumni.activation = _sum_score
            alumni.active = _sum_score
            # 名次， 名次率
            alumni_rate, alumni_ranking = score_alumni_score(person_id)
            print(_sum_score, alumni_rate, alumni_ranking)
            alumni.rank_alumni_score = alumni_ranking
            alumni.prank_alumni_score = alumni_rate

            alumni.alumni_score = task_person_reputation(alumni_doc.get("_id"))
            # alumni.save()

    score_alumni_by_redis()


def partial_update_alumni_metric(person_id: str):
    """
    人才的指标统计: 身份 排名 （埋点：活动管理的人接口，人列表）"
    """
    register_docs = col_register.find({"person_id": ObjectId(person_id), "is_attended": True})
    register = list(register_docs)
    alumni_doc = col_alumni.find_one({'_id': ObjectId(person_id)},
                                     {"rank_alumni_score": 0, "rank_alumni_score": 0})

    _register = {act.get("activity_id") for act in register}
    number_of_applicants = len(_register) if _register else 0  # 累计参与活动
    _score = []
    for reg in register:
        act = col_activity.find_one({"_id": reg["activity_id"], "is_attended": True})
        if act:
            score_act = act.get("importance_of_activities", "常规")
            score_role = reg.get("level_of_role", "一般参与")
            active_score = activity_import.get(score_act, 1) * role_import.get(score_role, 1)
            _score.append(active_score)

    _sum_score = sum(_score)
    _key = f"ranker"
    offset = random.random()
    t2 = int((time.time() + offset) * 1000_1000)
    score = f"{_sum_score}.{t2}"

    _mapping = {person_id: float(score)}
    aredis.zadd(_key, _mapping)

    activation = _sum_score
    active = _sum_score
    # 名次， 名次率
    alumni_rate, alumni_ranking = score_alumni_score(person_id)
    rank_alumni_score = alumni_ranking
    prank_alumni_score = alumni_rate
    belongs_inst = alumni_doc.get("belongs_inst", [-1])
    reputation = task_person_reputation(alumni_doc.get("_id"))
    update_data = {"number_of_applicants": number_of_applicants, "activation": activation, "active": active,
                   'alumni_score': reputation,
                   "belongs_inst": belongs_inst,
                   "rank_alumni_score": rank_alumni_score, "prank_alumni_score": prank_alumni_score,
                   "update_at": datetime.utcnow()}
    try:
        es_client.update(Index_KJJ_ALUMNI, id=person_id, body={"doc": update_data}, retry_on_conflict=5)
        score_alumni_by_redis(person_id)
    except elasticsearch.exceptions.NotFoundError:
        job_update_alumni_to_es(person_id)


def partial_update_activity_metric(act_id: str):
    members = col_register.count_documents({"activity_id": ObjectId(act_id), "is_attended": True})
    update_data = {
        "number_of_applicants": members, "update_at": datetime.utcnow()
    }

    try:
        es_client.update(Index_KJJ_ACTIVITY_REGISTER, id=act_id, routing=1, body={"doc": update_data},
                         retry_on_conflict=3)

    except elasticsearch.exceptions.NotFoundError as e:
        job_update_activity(act_id)


@celery_app.task()
def job_task_institution_overview():
    """每周一次"""
    year = datetime.utcnow().year
    for inst_id in col_institution.find({"level": 1}).distinct("_id"):
        task_update_inst_alumni_agg(inst_id=str(inst_id), year=year)
        task_update_inst_act_agg(inst_id=str(inst_id), year=year)
        _key = f"ranker"
        aredis.delete(_key)
        score_alumni_by_redis()


@celery_app.task()
def job_task_alumni_by_redis():
    _key = f"ranker"
    aredis.delete(_key)
    score_alumni_by_redis()


def score_alumni_by_redis(person_ids: Union[List[str], str, None] = None):
    """提前 排序的得分写入redis"""
    if isinstance(person_ids, str):
        person_ids = [person_ids]

    _key = f"ranker"
    query = {"is_deleted": {"$ne": True}, "alumni_score": {"$gt": 0}}
    if person_ids:
        query["_id"] = {"$in": [ObjectId(p) for p in person_ids]}
    for person in col_alumni.find(query):
        from_base_dt = person.get("update_time") or datetime.utcnow() - timedelta(days=1)
        member = str(person['_id'])
        _alumni_score = person.get("alumni_score", 1)
        score = int(_alumni_score)
        # 确保float 不重复
        offset = random.random()
        t2 = int((time.time() - from_base_dt.timestamp() + offset) * 1000_1000)
        score = f"{score}.{t2}"
        _mapping = {member: float(score)}
        aredis.zadd(_key, _mapping)


def score_alumni_score(person_id: str):
    _key = f"ranker"
    # 名次
    person_idx = aredis.zrevrank(_key, person_id)

    person_score = aredis.zscore(_key, person_id)

    if person_idx is None or person_score is None:
        return -1, -1
    p_score = int(person_score)

    def get_max_group(s):
        # 获得当前分数的最大排名
        p_idx = aredis.zrevrank(_key, s)

        return p_idx

    person_rank_by_score = aredis.zrevrangebyscore(_key, p_score + 1, p_score)
    # 获得当前分数的最大排名
    alumni_rank = get_max_group(person_rank_by_score[0])
    final_rank = -1
    if alumni_rank is not None:
        final_rank = alumni_rank + 1
    # 活动当前
    total_person = aredis.zcount(_key, '-inf', '+inf')
    # 名次占比
    final_alumni_rate = math.ceil(final_rank / total_person * 100)
    print(final_rank, total_person, )
    print(final_alumni_rate, final_rank)
    return final_alumni_rate, final_rank


if __name__ == '__main__':
    # job_update_activity("630df70581b00bdfb51f2004")
    # job_update_activity_contacts("630df70581b00bdfb51f2004", "630df4bba29c1327a1d75a7e")
    # print(time.time() - t)
    # job_delete_contact_by_act("630df70581b00bdfb51f2004", "630df4bba29c1327a1d757c7")
    # job_task_alumni_rank_score(["634a475f0b36fe19842aa580"])
    # job_task_alumni_rank_score("6361dd8c0315ad68d439f8b9")
    # job_update_alumni_to_es("63b54385fc500af259229217")
    # job_delete_alumni(["63b54385fc500af259229217"])
    # partial_update_activity_metric("634e7025ddce14913a351252")
    # workflow_delete_alumni(['634a475c0b36fe19842aa518'])
    # workflow_creat_edit_alumni(['63cf58d40f53b155c379534b'])
    # partial_update_alumni_metric("634a475b0b36fe19842aa4f8")
    # print(score_alumni_score("6371975c67036204fa0c82a9")
    score_alumni_score("635f66540315ad68d439f3f7")
